#include "amsnake.h"
#include "ui_amsnake.h"

SnakeNode::SnakeNode()
{

}

SnakeNode::SnakeNode(int x, int y, QColor color)
{
    this->x = x;
    this->y = y;
    this->color = color;
}

bool SnakeNode::operator ==(const SnakeNode &other) const
{
    return (this->x == other.x && this->y == other.y);
}

bool SnakeNode::operator !=(const SnakeNode &other) const
{
    return !(*this == other);
}

//拷贝构造函数
AutoSnake::AutoSnake(int sideLen, int stepLen)
{
    this->sideLen = sideLen;
    this->stepLen = stepLen;

    init();
}

AutoSnake::~AutoSnake()
{
    delete food;

    delete pathBoard;
    delete path;
    delete board;
}

void AutoSnake::init()
{
    //path_board初始化
    pathBoard = new bool*[sideLen];
    for(int i = 0; i < sideLen; i++)
    {
        pathBoard[i] = new bool[sideLen];
        for(int j = 0; j < sideLen; j++)
            pathBoard[i][j] = false;
    }

    //path初始化
    path = new qint8*[sideLen];
    for(int i = 0; i < sideLen; i++)
        path[i] = new qint8[sideLen];
    for (int i = 0; i < sideLen; i += 2) {
        for (int j = 0; j < sideLen; j += 2) {
            path[i][j] = Right;
            path[i + 1][j + 1] = Left;
            path[i][j + 1] = Up;
            path[i + 1][j] = Down;
        }
    }

    //board初始化
    board = new bool*[sideLen];
    for(int i = 0; i < sideLen; i++)
    {
        board[i] = new bool[sideLen];
        for(int j = 0; j < sideLen; j++)
            board[i][j] = false;
    }

    //头插法，初始长度为2
    for (int i = 0; i < 2; i++)
    {
        snake.push_front(SnakeNode(i, 0));
        board[i][0] = true;
    }
    food = new SnakeNode;

    setPathBoard(SnakeNode(0, 0), true);
}

//创建食物
void AutoSnake::createFood()
{
    do
    {	//随机创建食物
        food->x = qrand() % sideLen;
        food->y = qrand() % sideLen;
    } while(board[food->x][food->y]);

    delPath();
    formPath();
}

/*
 * 贪吃蛇前进
 * 参数：
 *	node：前进的节点
 *	isEatFood：是否吃到食物
 */
void AutoSnake::forward(SnakeNode &node, bool isEatFood)
{
    //如果没吃到食物，则删除尾部节点
    if(!isEatFood)
    {
        setBoardFalse(snake.back());
        snake.pop_back();
        setBoardTrue(node);
        snake.push_front(node);
        delPath();
        formPath();
    }
    else
    {
        setBoardTrue(node);
        snake.push_front(node);
        if(snake.size() != sideLen*sideLen)
            createFood();		//吃满就不创建了
    }
}

/*
 * 移动
 * 返回值：
 *	0：正常前进
 *	1：撞到自己
 *	2：撞到墙
 *	4：吃到食物
 */
int AutoSnake::move()
{
    //创建前进节点
    SnakeNode nextHead = snake.front();

    //判断前进方向
    //前进节点赋值
    int tmpDir = path[nextHead.x][nextHead.y];
    nextHead.x += dir[tmpDir][0];
    nextHead.y += dir[tmpDir][1];

    //是否吃到食物
    //蛇前进
    if(nextHead.x == food->x && nextHead.y == food->y)
    {
        forward(nextHead, true);
        return 1;		//吃到食物
    }
    forward(nextHead, false);
    return 0;			//正常前进
}

//一次性将一个环添加或删除时，修改path_board
void AutoSnake::setPathBoard(SnakeNode x, bool t)
{
    pathBoard[x.x][x.y] = t;
    pathBoard[x.x + 1][x.y] = t;
    pathBoard[x.x][x.y + 1] = t;
    pathBoard[x.x + 1][x.y + 1] = t;
}

//某一极小环路是否存在蛇的身体，不存在的环路要拆除
bool AutoSnake::matrixNoPoint(SnakeNode x)
{
    return board[x.x][x.y] == false && board[x.x + 1][x.y] == false &&
        board[x.x][x.y + 1] == false && board[x.x + 1][x.y + 1] == false;
}

//两个极小环路相接的环路是否一个在路径里，一个在路径外，这是可添加路径的判断依据
bool AutoSnake::pathBoardNotAllTrue(SnakeNode x)
{
    return !(pathBoard[x.x][x.y] && pathBoard[x.x + 1][x.y + 1]);
}

//添加路径
void AutoSnake::addPathMatrix(SnakeNode x)
{
    if (x.x % 2)
    {
        if (pathBoardNotAllTrue(x))
        {
            path[x.x][x.y] = Right;
            path[x.x + 1][x.y + 1] = Left;
            setPathBoard(SnakeNode(x.x - 1, x.y), true);
            setPathBoard(SnakeNode(x.x + 1, x.y), true);
        }
    }
    else
    {
        if (pathBoardNotAllTrue(x))
        {
            path[x.x + 1][x.y] = Down;
            path[x.x][x.y + 1] = Up;
            setPathBoard(SnakeNode(x.x, x.y + 1), true);
            setPathBoard(SnakeNode(x.x, x.y - 1), true);
        }
    }
}

//判断是否为可剪除的环路
void AutoSnake::checkIfDel(SnakeNode x)
{
    if (matrixNoPoint(x))
        setPathBoard(x, false);
}

void AutoSnake::delPathMatrix(SnakeNode x)
{
    if (x.x % 2)
    {
        path[x.x][x.y] = Down;
        path[x.x + 1][x.y + 1] = Up;
        checkIfDel(SnakeNode(x.x - 1, x.y));
        checkIfDel(SnakeNode(x.x + 1, x.y));
    }
    else
    {
        path[x.x + 1][x.y] = Left;
        path[x.x][x.y + 1] = Right;
        checkIfDel(SnakeNode(x.x, x.y - 1));
        checkIfDel(SnakeNode(x.x, x.y + 1));
    }
}

//路径生成
void AutoSnake::formPath()
{
    SnakeNode head = snake.front();
    head.x = head.x & 0xfffe;
    head.y = head.y & 0xfffe;

    SnakeNode end = *food;
    end.x = end.x & 0xfffe;
    end.y = end.y & 0xfffe;

    short dx;
    if (head.x > end.x) dx = -2;
    else dx = 2;

    short dy;
    if (head.y > end.y) dy = -2;
    else dy = 2;

    while (head.x != end.x)
    {
        short x = head.x;
        head.x += dx;
        addPathMatrix(SnakeNode((x + head.x) / 2, head.y));
    }

    while (head.y != end.y)
    {
        short y = head.y;
        head.y += dy;
        addPathMatrix(SnakeNode(end.x, (head.y + y)/2));
    }
}

//删除路径
void AutoSnake::delPath()
{
    for (short i = 0; i <= sideLen - 2; i += 2)
        for (short j = 1; j < sideLen - 2; j += 2)
        {
            if (matrixNoPoint(SnakeNode(i, j - 1)) || matrixNoPoint(SnakeNode(i, j + 1)))
                delPathMatrix(SnakeNode(i, j));
            if (matrixNoPoint(SnakeNode(j - 1, i)) || matrixNoPoint(SnakeNode(j + 1, i)))
                delPathMatrix(SnakeNode(j, i));
        }
}

bool AutoSnake::isNotAllTrue(bool **tmp)
{
    for (int i = 0; i < sideLen; i++)
        for (int j = 0; j < sideLen; j++)
            if (tmp[i][j] == false)
                return true;
    return false;
}

bool AutoSnake::isValidPoint(SnakeNode tmp)
{
    return !((tmp.x & 0xfff0) || (tmp.y & 0xfff0));
}

void AutoSnake::setBoardTrue(SnakeNode X)
{
    board[X.x][X.y] = true;
}

void AutoSnake::setBoardFalse(SnakeNode X)
{
    board[X.x][X.y] = false;
}

AMSnake::AMSnake(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::AMSnake)
{
    ui->setupUi(this);

    setWindowTitle("此处有观赏蛇");
    endGamestatus = -2;		//游戏未开始
}

AMSnake::~AMSnake()
{
    delete ui;

    if(endGamestatus == -2) return;

    delete snake;

    delete bgPlaylist;
    delete bgPlayer;

    delete jbPlaylist;
    delete jbPlayer;

    delete swPlaylist;
    delete swPlayer;

    delete timer;
}

//游戏初始化
void AMSnake::initGame()
{
    //数据初始化
    score = 0;
    add = 10;
    grade = 1;
    sleeptime = 155;

    //音频初始化
    bgPlaylist = new QMediaPlaylist;
    bgPlaylist->addMedia(QUrl("qrc:/mp3/1.mp3"));
    bgPlaylist->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop);
    bgPlaylist->setCurrentIndex(1);
    bgPlayer = new QMediaPlayer;
    bgPlayer->setPlaylist(bgPlaylist);
    bgPlayer->play();

    jbPlaylist = new QMediaPlaylist;
    jbPlaylist->addMedia(QUrl("qrc:/mp3/jinbi.mp3"));
    jbPlaylist->setPlaybackMode(QMediaPlaylist::CurrentItemOnce);
    jbPlaylist->setCurrentIndex(1);
    jbPlayer = new QMediaPlayer;
    jbPlayer->setPlaylist(jbPlaylist);

    swPlaylist = new QMediaPlaylist;
    swPlaylist->addMedia(QUrl("qrc:/mp3/siwang.mp3"));
    swPlaylist->setCurrentIndex(1);
    swPlayer = new QMediaPlayer;
    swPlayer->setPlaylist(swPlaylist);

    //加载金币图片
    jinbi.load(":/image/jinbi.jpg");

    //消息对话框初始化
    msgBox.setWindowTitle("提示 O(∩_∩)O ：");
    msgBox.setStandardButtons(QMessageBox::Ok);
    msgBox.setModal(true);

    //蛇身初始化
    snake = new AutoSnake(mapLength, mapSize);

    //创建食物
    snake->createFood();

    //启动定时器
    timer = new QTimer;
    timer->start(sleeptime);
    connect(timer, SIGNAL(timeout()), this, SLOT(snakeMove()));

    ui->monLabel->setText(QString("金币：%1").arg(score));

    endGamestatus = 0;
}

//绘制游戏界面
void AMSnake::paintEvent(QPaintEvent *event)
{
    if(endGamestatus == -2) return;

    QPainter painter(this);
    QPen pen;
    QBrush brush;
    QFont font("方正舒体",12,QFont::ExtraLight,false);

    //反锯齿
    painter.setRenderHint(QPainter::Antialiasing);

    pen.setColor(qRgb(195, 195, 195));		//边框颜色
    pen.setJoinStyle(Qt::MiterJoin);
    pen.setWidth(mapSize);
    painter.setPen(pen);
    brush.setColor(Qt::white);		//背景颜色
    brush.setStyle(Qt::SolidPattern);
    painter.setBrush(brush);
    painter.drawRect(mapSize / 2, mapSize / 2, (mapLength+1)*mapSize, (mapLength+1)*mapSize);

    pen.setColor(qRgb(0, 0, 0));			//网格颜色
    pen.setWidth(0);
    painter.setPen(pen);
    for (int i = 2*mapSize; i <= mapLength*mapSize; i += mapSize)
        for (int j = 2*mapSize; j <= mapLength*mapSize; j += mapSize)
            painter.fillRect(i - 1, j - 1, mapSize/10, mapSize/10,Qt::black);

    //绘制蛇身
    painter.translate(mapSize, mapSize);
    QList<SnakeNode>::iterator it = snake->snake.begin();
    pen.setColor(0x006400);
    painter.setPen(pen);
    brush.setColor(0x006400);
    brush.setStyle(Qt::SolidPattern);
    painter.setBrush(brush);
    painter.drawRect(it->x*mapSize, it->y*mapSize, mapSize, mapSize);
    it++;
    while (it + 1 != snake->snake.end())
    {
        pen.setColor(0x00FF00);
        painter.setPen(pen);
        brush.setColor(0x00FF00);
        brush.setStyle(Qt::SolidPattern);
        painter.setBrush(brush);
        painter.drawRect(it->x*mapSize, it->y*mapSize, mapSize, mapSize);
        it++;
    }
    pen.setColor(0x98FB98);
    painter.setPen(pen);
    brush.setColor(0x98FB98);
    brush.setStyle(Qt::SolidPattern);
    painter.setBrush(brush);
    painter.drawRect(it->x*mapSize, it->y*mapSize, mapSize, mapSize);

    painter.setPen(Qt::NoPen);
    painter.setBrush(getColor());
    //绘制食物
    painter.drawRect(snake->food->x*mapSize, snake->food->y*mapSize, mapSize, mapSize);


    QWidget::paintEvent(event);
}
QColor AMSnake::getColor()
{//获取随机颜色
    static const QRgb colorTable[8] = {
        0xFFE4E1, 0xFFE4E1, 0xDDA0DD, 0xFFFFE0,
        0xB0E0E6, 0xB0E0E6, 0xDDA0DD, 0xFFFFE0
    };
    QColor color;
    color = colorTable[qrand()%8];
    return color;
}

//游戏结束
void AMSnake::endGame()
{
    //停止背景音乐
    bgPlayer->stop();
    timer->stop();

    switch (endGamestatus)
    {	//游戏结束的情况，-2:未开始; -1:暂停; 0:正常; 3:主动退出游戏; 5:吃满全屏，游戏胜利
    case 3:
        swPlayer->play();
        msgBox.setText("您的游戏已经结束了！");
        msgBox.setIcon(QMessageBox::Icon::NoIcon);
        msgBox.exec();
        swPlayer->stop();
        break;
    case 5:
        msgBox.setText("恭喜，吃满全屏，游戏胜利！");
        msgBox.setIcon(QMessageBox::Icon::NoIcon);
        msgBox.exec();
        break;
    default:
        break;
    }

    //关闭当前游戏窗口，并返回主菜单页面
    this->close();
    emit showMainWindow();
}

//蛇移动
void AMSnake::snakeMove()
{
    if(endGamestatus == -2) return;

    switch(snake->move())
    {
    case 1:
        jbPlayer->stop();
        jbPlayer->play();
        score += add;
        ui->monLabel->setText(QString("金币：%1").arg(score));
        if(snake->snake.size() == mapLength*mapLength)
        {	//吃满全屏
            endGamestatus = 5;
            endGame();
            return;
        }
        break;
    default:
        break;
    }

    update();
}

//按键按下事件
void AMSnake::keyReleaseEvent(QKeyEvent *event)
{
    if(endGamestatus == -2) return;

    switch(event->key()){
    case Qt::Key_Space:
        if(endGamestatus == -1){
            timer->start(sleeptime);
            endGamestatus = 0;
        }else if(endGamestatus == 0){
            timer->stop();
            endGamestatus = -1;
        }
        break;
    case Qt::Key_Escape:
        endGamestatus = 3;
        endGame();
        break;
    case Qt::Key_F1:
        if (sleeptime > 30)
        {	//速度不能太快
            sleeptime = sleeptime - 30;
            timer->stop();
            timer->start(sleeptime);
            add = add + 2;
        }
        break;
    case Qt::Key_F2:
        //松手检测
        if (sleeptime < 270)
        {	//单价不能小于0
            sleeptime = sleeptime + 30;
            timer->stop();
            timer->start(sleeptime);
            add = add - 2;
        }
        break;
    default:
        break;
    }
}

//游戏开始
bool AMSnake::play()
{
    bool isOK;
    QString mapLenStr;
    for (;;)
    {
        mapLenStr = QInputDialog::getText(this, "初始条件设置", "请输入贪吃蛇地图的边长（必须为偶数）：",
                                        QLineEdit::Normal, "16", &isOK);
        if(isOK)
        {
            if(mapLenStr.toInt() & 1)
            {	//如果是奇数，报错并重新获取
                QMessageBox::information(this, "提示 O(∩_∩)O ：", mapLenStr + "是奇数，请输入偶数！",
                                        QMessageBox::Yes, QMessageBox::Yes);
                continue;
            }
            else
            {
                mapLength = mapLenStr.toInt();
                mapSize = 680 / (mapLength+2);
                initGame();
                this->show();
                return true;
            }
        }
        else
        {	//如果取消了，直接退出自动贪吃蛇
            QMessageBox::information(this, "提示：", "退出成功！",
                                    QMessageBox::Yes, QMessageBox::Yes);
            return false;
        }
    }
}

//静音复选框
void AMSnake::on_soundOnOffCheckBox_stateChanged(int arg1)
{
    switch(arg1)
    {
    case Qt::CheckState::Checked:		//选中
        //开启静音
        bgPlayer->stop();
        jbPlayer->stop();
        swPlayer->stop();
        bgPlayer->setPlaylist(nullptr);
        jbPlayer->setPlaylist(nullptr);
        swPlayer->setPlaylist(nullptr);
        //复选框内容改为取消静音
        ui->soundOnOffCheckBox->setText("取消静音");
        focusNextPrevChild(true);
        break;
    case Qt::CheckState::Unchecked:		//未选中
        //取消静音
        bgPlayer->setPlaylist(bgPlaylist);
        jbPlayer->setPlaylist(jbPlaylist);
        swPlayer->setPlaylist(swPlaylist);
        bgPlayer->play();
        //复选框内容改为静音
        ui->soundOnOffCheckBox->setText("静音");
        focusNextPrevChild(true);
        break;
    default:
        break;
    }
}


